#include "export_time.hh"
#include "../../../detail/local_system_time_impl.hh"
#include "../../../box/service_box.hh"

kratos::lua::LuaTime::LuaTime(kratos::service::ServiceBox *box, lua_State *L,
                              LuaServiceImpl *service) {
  box_ = box;
  L_ = L;
  service_ = service;
}

kratos::lua::LuaTime::~LuaTime() {}

auto kratos::lua::LuaTime::do_register() -> bool {
  set_class(L_, this, "_box_time");
  times_ptr_ = kratos::make_unique_pool_ptr<kratos::time::LocalTimeImpl>();

  LuaUtil::register_function(L_, "kratos_get_millionsecond",
                             &LuaTime::lua_get_millionsecond);
  LuaUtil::register_function(L_, "kratos_get_second", &LuaTime::lua_get_second);
  LuaUtil::register_function(L_, "kratos_utc_diff_second",
                             &LuaTime::lua_utc_diff_second);
  LuaUtil::register_function(L_, "kratos_diff_days_now",
                             &LuaTime::lua_diff_days_now);
  LuaUtil::register_function(L_, "kratos_diff_days", &LuaTime::lua_diff_days);
  LuaUtil::register_function(L_, "kratos_get_date", &LuaTime::lua_get_date);
  LuaUtil::register_function(L_, "kratos_date_from_string",
                             &LuaTime::lua_date_from_string);
  LuaUtil::register_function(L_, "kratos_data_from_time",
                             &LuaTime::lua_data_from_time);
  LuaUtil::register_function(L_, "kratos_is_same_day",
                             &LuaTime::lua_is_same_day);
  LuaUtil::register_function(L_, "kratos_is_same_week",
                             &LuaTime::lua_is_same_week);
  LuaUtil::register_function(L_, "kratos_is_same_month",
                             &LuaTime::lua_is_same_month);

  box_->write_log_line(klogger::Logger::VERBOSE,
                       "[lua][time]Time module installed");

  return true;
}

auto kratos::lua::LuaTime::update(std::time_t ms) -> void {}

auto kratos::lua::LuaTime::do_cleanup() -> void {}

int kratos::lua::LuaTime::lua_get_millionsecond(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *time = LuaExportClass::get_class<LuaTime>(l, "_box_time");
  if (!time) {
    return pusher.return_value();
  }
  return pusher.return_value(time->times_ptr_->get_millionsecond());
}

int kratos::lua::LuaTime::lua_get_second(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *time = LuaExportClass::get_class<LuaTime>(l, "_box_time");
  if (!time) {
    return pusher.return_value();
  }
  return pusher.return_value(time->times_ptr_->get_second());
}

int kratos::lua::LuaTime::lua_utc_diff_second(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *time = LuaExportClass::get_class<LuaTime>(l, "_box_time");
  if (!time) {
    return pusher.return_value();
  }
  return pusher.return_value(time->times_ptr_->utc_diff_second());
}

int kratos::lua::LuaTime::lua_diff_days_now(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *time = LuaExportClass::get_class<LuaTime>(l, "_box_time");
  if (!time) {
    return pusher.return_value();
  }
  if (!lua_isinteger(l, -1)) {
    return pusher.return_value();
  }
  auto t = (std::time_t)lua_tointeger(l, -1);
  return pusher.return_value(time->times_ptr_->diff_days(t));
}

int kratos::lua::LuaTime::lua_diff_days(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *time = LuaExportClass::get_class<LuaTime>(l, "_box_time");
  if (!time) {
    return pusher.return_value();
  }
  if (!lua_isinteger(l, -1) || !lua_isinteger(l, -2)) {
    return pusher.return_value();
  }
  auto t1 = (std::time_t)lua_tointeger(l, -2);
  auto t2 = (std::time_t)lua_tointeger(l, -1);
  return pusher.return_value(time->times_ptr_->diff_days(t1, t2));
}

int kratos::lua::LuaTime::lua_get_date(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *time = LuaExportClass::get_class<LuaTime>(l, "_box_time");
  if (!time) {
    return pusher.return_value();
  }
  return pusher.return_value(time->times_ptr_->get_date().get());
}

int kratos::lua::LuaTime::lua_date_from_string(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *time = LuaExportClass::get_class<LuaTime>(l, "_box_time");
  if (!time) {
    return pusher.return_value();
  }
  if (!lua_isstring(l, -1)) {
    return pusher.return_value();
  }
  auto s = lua_tostring(l, -1);
  try {
    return pusher.return_value(time->times_ptr_->from(s).get());
  } catch (...) {
    return pusher.return_value();
  }
}

int kratos::lua::LuaTime::lua_data_from_time(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *time = LuaExportClass::get_class<LuaTime>(l, "_box_time");
  if (!time) {
    return pusher.return_value();
  }
  if (!lua_isinteger(l, -1)) {
    return pusher.return_value();
  }
  auto t = (std::time_t)lua_tointeger(l, -1);
  try {
    return pusher.return_value(time->times_ptr_->from(t).get());
  } catch (...) {
    return pusher.return_value();
  }
}

int kratos::lua::LuaTime::lua_is_same_day(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *time = LuaExportClass::get_class<LuaTime>(l, "_box_time");
  if (!time) {
    return pusher.return_value();
  }
  if (!lua_isinteger(l, -1) || !lua_isinteger(l, -2)) {
    return pusher.return_value();
  }
  auto t1 = (std::time_t)lua_tointeger(l, -2);
  auto t2 = (std::time_t)lua_tointeger(l, -1);
  return pusher.return_value(time->times_ptr_->in_same_day(t1, t2));
}

int kratos::lua::LuaTime::lua_is_same_week(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *time = LuaExportClass::get_class<LuaTime>(l, "_box_time");
  if (!time) {
    return pusher.return_value();
  }
  if (!lua_isinteger(l, -1) || !lua_isinteger(l, -2)) {
    return pusher.return_value();
  }
  auto t1 = (std::time_t)lua_tointeger(l, -2);
  auto t2 = (std::time_t)lua_tointeger(l, -1);
  return pusher.return_value(time->times_ptr_->in_same_week(t1, t2));
}

int kratos::lua::LuaTime::lua_is_same_month(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *time = LuaExportClass::get_class<LuaTime>(l, "_box_time");
  if (!time) {
    return pusher.return_value();
  }
  if (!lua_isinteger(l, -1) || !lua_isinteger(l, -2)) {
    return pusher.return_value();
  }
  auto t1 = (std::time_t)lua_tointeger(l, -2);
  auto t2 = (std::time_t)lua_tointeger(l, -1);
  return pusher.return_value(time->times_ptr_->in_same_month(t1, t2));
}
